﻿using HelixToolkit.Wpf.SharpDX;
using WpfModelEditor.Core.Plugins;
using HelixToolkit.Wpf.SharpDX.Animations;
using System.Collections.ObjectModel;
using System;
using SharpDX;
using System.Threading.Tasks;
using System.Linq;
using System.IO;
using Fluent;
using Serilog;
using AvalonDock.Layout;
using HelixToolkit.Wpf.SharpDX.Model.Scene;
using WpfModelEditor;
using System.Windows;
using System.Collections.Generic;
using HelixToolkit.Wpf.SharpDX.Model;
using System.Diagnostics;
using HelixToolkit.Wpf.SharpDX.Controls;

namespace WpfModelEditor
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : IHost, IHostUI
    {
        private bool _loaded = false;
        public MainWindow()
        {
            InitializeComponent();
            presenter.Content = new SceneNodeGroupModel3D();
            var path = Path.Combine(Program.CurrentDir, "Cubemap_Grandcanyon.dds");
            if (File.Exists(path))
            {
                envMap.Texture = TextureModel.Create(path);
            }
            CameraType = CameraType.PerspectiveCamera;
            (Camera as PerspectiveCamera).FarPlaneDistance = 5000;
            EffectsManager = new DefaultEffectsManager();
            _loaded = true;
            InitPlugins();
            InitMenus();
        }
        #region 属性
        private CompositionTargetEx compositeHelper = new CompositionTargetEx();
        private IAnimationUpdater _selectedAnimation;
        public IAnimationUpdater SelectedAnimation
        {
            get { return _selectedAnimation; }
            set
            {
                if (_selectedAnimation != value)
                {
                    _selectedAnimation = value;
                    StopAnimation();
                }
            }
        }

        public string _mode = "Select";
        public string Mode
        {
            get
            {
                return _mode;
            }
            set
            {
                if (_mode != value)
                {
                    _mode = value;
                    FireEvent("ModeChanged");
                }
            }
        }
        public bool RenderEnvironmentMap
        {
            get { return EnvironmentMap.IsRendering; }
            set
            {
                if (EnvironmentMap.IsRendering != value)
                {
                    EnvironmentMap.IsRendering = value;
                    UpdateRenderEnvironmentMap();
                }
            }
        }
        private object _selectedObject { get; set; }
        public object SelectedObject
        {
            get { return _selectedObject; }
            set
            {
                if (_selectedObject != value)
                {
                    SetSelected(_selectedObject, false);

                    _selectedObject = value;
                    if (value != null)
                    {
                        SetSelected(_selectedObject, true);
                    }
                    FireEvent("SelectedChanged", value);
                }
            }
        }
        public List<IPlugin> Plugins { get { return Program.Plugins; } }
        public List<IIoPlugin> IoPlugins { get { return Program.IoPlugins; } }
        public TransformManipulator3D Manipulator { get { return manipulator; } }
        public ShadowMap3D ShadowMap3D { get { return shadow; } }
        public AxisPlaneGridModel3D AxisPlaneGridModel3D { get { return plane; } }
        public LayoutAnchorablePane BottomPanel { get { return panelBottom; } }
        public LayoutAnchorablePane LeftPanel { get { return panelLeft; } }
        public LayoutAnchorablePane RightPanel { get { return panelRight; } }
        public Window Window { get { return this; } }
        public EnvironmentMap3D EnvironmentMap
        {
            get { return envMap; }
        }
        private IEffectsManager _effectsManager;
        public IEffectsManager EffectsManager
        {
            get { return _effectsManager; }
            set
            {
                if (_effectsManager != value)
                {
                    _effectsManager = value;
                    ActiveView.EffectsManager = value;
                    FireEvent("EffectsManagerChanged", value);
                }
            }
        }
        public ObservableCollection<IAnimationUpdater> Animations { get; } = new ObservableCollection<IAnimationUpdater>();
        public Camera Camera
        {
            get { return ActiveView.Camera; }
            set
            {
                ActiveView.Camera = value;
            }
        }
        private CameraType _cameraType = 0;
        public CameraType CameraType
        {
            get
            {
                return _cameraType;
            }
            set
            {
                if (_cameraType != value)
                {
                    _cameraType = value;
                    switch (value)
                    {
                        case CameraType.PerspectiveCamera:
                            Camera = new PerspectiveCamera();
                            break;
                        case CameraType.OrthographicCamera:
                            Camera = new OrthographicCamera();
                            break;
                    }
                    FireEvent("CameraTypeChanged", null);
                }
            }
        }

        public SceneNodeGroupModel3D Model
        {
            get { return presenter.Content as SceneNodeGroupModel3D; }
        }
        private bool _isBusy = false;
        public bool IsBusy
        {
            get { return _isBusy; }
            set
            {
                if (_isBusy != value)
                {
                    _isBusy = value;
                    IsEnabled = !value;
                    FireEvent("IsBusy", IsBusy);
                }
            }
        }
        public BoundingBox ModelBound { get; private set; } = new BoundingBox();
        public Viewport3DX ActiveView { get { return view; } }
        public IHostUI UI { get { return this; } }
        #region UI
        public Ribbon Ribbon { get { return ribbon; } }
        public Backstage Backstage { get { return backstage; } }
        public BackstageTabControl BackstageTabControl { get { return backstageTabControl; } }
        public Fluent.StatusBar StatusBar
        {
            get { return statusBar; }
        }
        public RibbonTabItem GetOrCreateRibbonTabItem(string header)
        {
            var item = Ribbon.Tabs.FirstOrDefault(p => header.Equals(p.Header));
            if (item != null)
                return item;
            item = new RibbonTabItem
            {
                Header = header,
            };
            Ribbon.Tabs.Add(item);
            return item;
        }
        #endregion
        #endregion
        private void InitMenus()
        {
        }
        private void UpdateRenderEnvironmentMap()
        {
            var value = RenderEnvironmentMap;
            if (Model == null || Model.SceneNode == null)
                return;
            foreach (var node in Model.SceneNode.Traverse())
            {
                if (node is MaterialGeometryNode m)
                {
                    if (m.Material is PBRMaterialCore pbr)
                        pbr.RenderEnvironmentMap = value;
                    else if (m.Material is PhongMaterialCore phong)
                        phong.RenderEnvironmentMap = value;
                }
            }
        }
        private void SetSelected(object obj, bool selected)
        {
            if (obj == null)
                return;
            if (obj is GeometryNode)
            {
                var mn = obj as GeometryNode;
                mn.PostEffects = selected ? $"highlight[color:#FFFF00]" : null;
            }
            else if (obj is GeometryModel3D)
            {
                var mn = obj as GeometryModel3D;
                mn.PostEffects = selected ? $"highlight[color:#FFFF00]" : null;
            }
        }
        public void FireEvent(string eventName, object data = null)
        {
            if (!_loaded)
                return;
            switch (eventName)
            {
                case "SceneChanged":
                    {
                        UpdateRenderEnvironmentMap();
                        FocusCameraToScene();
                    }
                    break;
            }
            foreach (var plugin in Program.Plugins)
            {
                plugin.OnEvent(eventName, data);
            }
        }
        private void InitPlugins()
        {
            foreach (var plugin in Program.IoPlugins)
            {
                try
                {
                    plugin.Initialize(this);
                }
                catch (Exception ex)
                {
                    Log.Error(ex.ToString());
                }
            }
            foreach (var plugin in Program.Plugins)
            {
                try
                {
                    plugin.Initialize(this);
                }
                catch (Exception ex)
                {
                    Log.Error(ex.ToString());
                }
            }
        }

        public void StartAnimation()
        {
            compositeHelper.Rendering -= CompositeHelper_Rendering;
            compositeHelper.Rendering += CompositeHelper_Rendering;
        }
        public void StopAnimation()
        {
            compositeHelper.Rendering -= CompositeHelper_Rendering;
        }
        private void CompositeHelper_Rendering(object sender, System.Windows.Media.RenderingEventArgs e)
        {
            if (_selectedAnimation != null)
            {
                _selectedAnimation.Update(Stopwatch.GetTimestamp(), Stopwatch.Frequency);
            }
        }
        public void Clear()
        {
            SelectedAnimation = null;
            Animations.Clear();
            var oldNode = Model.SceneNode.Items.ToArray();
            Model.Clear(false);
            Task.Run(() =>
            {
                foreach (var node in oldNode)
                {
                    node.Dispose();
                }
            });
            view.InvalidateRender();
            FireEvent("SceneChanged");
        }
        public void FocusCameraToNode(SceneNode node)
        {
            if (node.TryGetBound(out var bound))
            {
                ModelBound = bound;
                var maxWidth = Math.Max(Math.Max(ModelBound.Width, ModelBound.Height), ModelBound.Depth) * 3.5f;
                if (maxWidth == 0.0f)
                    maxWidth = 1.0f;
                var pos = ModelBound.Center + new Vector3(0, 0, maxWidth);
                Camera.Position = pos.ToPoint3D();
                Camera.LookDirection = (ModelBound.Center - pos).ToVector3D();
                Camera.UpDirection = Vector3.UnitY.ToVector3D();
                if (Camera is OrthographicCamera orthCam)
                {
                    orthCam.Width = maxWidth;
                }
            }
        }
        public void FocusCameraToScene()
        {
            if (Model.SceneNode.TryGetBound(out var bound))
            {
                ModelBound = bound;
                var maxWidth = Math.Max(Math.Max(ModelBound.Width, ModelBound.Height), ModelBound.Depth) * 3.5f;
                if (maxWidth == 0.0f)
                    maxWidth = 1.0f;
                var pos = ModelBound.Center + new Vector3(0, 0, maxWidth);
                Camera.Position = pos.ToPoint3D();
                Camera.LookDirection = (ModelBound.Center - pos).ToVector3D();
                Camera.UpDirection = Vector3.UnitY.ToVector3D();
                if (Camera is OrthographicCamera orthCam)
                {
                    orthCam.Width = maxWidth;
                }
            }
        }

        private void view_MouseDown3D(object sender, System.Windows.RoutedEventArgs e)
        {
            var ev = e as MouseDown3DEventArgs;
            if (ev == null)
                return;
            FireEvent("MouseDown", ev);
        }
        private void view_MouseUp3D(object sender, System.Windows.RoutedEventArgs e)
        {
            var ev = e as MouseUp3DEventArgs;
            if (ev == null)
                return;
            FireEvent("MouseUp", ev);
        }
    }
}
